/*
 * Copyright (C) 2019 ByteDance Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bytedance.scene.animation;

import com.bytedance.scene.Scene;
import com.bytedance.scene.State;
import com.bytedance.scene.navigation.NavigationScene;
import com.bytedance.scene.utlity.CancellationSignal;
import com.bytedance.scene.utlity.CancellationSignalList;
import com.bytedance.scene.utlity.Utility;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentTreeObserver;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by JiangQi on 7/30/18.
 */
public abstract class NavigationAnimationExecutor {
    protected ComponentContainer mAnimationViewGroup;

    public void setAnimationViewGroup(ComponentContainer viewGroup) {
        this.mAnimationViewGroup = viewGroup;
    }

    public abstract boolean isSupport(Class<? extends Scene> from, Class<? extends Scene> to);

    public final void executePushChange(final NavigationScene navigationScene,
                                        final Component rootView,
                                        final AnimationInfo fromInfo,
                                        final AnimationInfo toInfo,
                                        final CancellationSignalList cancellationSignal,
                                        final Runnable endAction) {
        navigationScene.requestDisableTouchEvent(true);
        final Component fromView = fromInfo.mSceneView;
        final Component toView = toInfo.mSceneView;
        if (fromInfo.mSceneState.value < State.VIEW_CREATED.value) {
             mAnimationViewGroup.addComponent(fromView);
        }

        final Runnable pushEndAction = () -> {
            navigationScene.requestDisableTouchEvent(false);

            if (fromInfo.mSceneState.value < State.VIEW_CREATED.value) {
                mAnimationViewGroup.removeComponent(fromView);
            }
            endAction.run();
        };
        cancellationSignal.setOnCancelListener(pushEndAction::run);

        executePushChangeCancelable(fromInfo, toInfo, pushEndAction, cancellationSignal.getChildCancellationSignal());
    }

    public final void executePopChange(final NavigationScene navigationScene,
                                       final Component rootView,
                                       final AnimationInfo fromInfo,
                                       final AnimationInfo toInfo,
                                       final CancellationSignalList cancellationSignal,
                                       final Runnable endAction) {
        navigationScene.requestDisableTouchEvent(true);
        final Runnable popEndAction = () -> {
            navigationScene.requestDisableTouchEvent(false);
            endAction.run();
        };

        cancellationSignal.setOnCancelListener(popEndAction::run);

        final Component fromView = fromInfo.mSceneView;
        final Component toView = toInfo.mSceneView;

        final boolean isFromViewReady = !(fromView.getWidth() == 0 || fromView.getHeight() == 0);
        boolean isToViewReady = !(toView.getWidth() == 0 || toView.getHeight() == 0);

        if (!isFromViewReady || !isToViewReady) {
            final CancellationSignal layoutCancellationSignal = cancellationSignal.getChildCancellationSignal();

            skipDrawUntilViewMeasureReady(rootView, layoutCancellationSignal, new Runnable() {
                @Override
                public void run() {
                    if (!isFromViewReady) {
                        Utility.removeFromParentView(fromView);
                        fromView.setVisibility(Component.HIDE);
                    }
                    if (!layoutCancellationSignal.isCanceled()) {
                        executePopChangeCancelable(fromInfo, toInfo, popEndAction, cancellationSignal.getChildCancellationSignal());
                    }
                }
            });

            if (!isFromViewReady) {
                mAnimationViewGroup.addComponent(fromView);
                fromView.setVisibility(Component.VISIBLE);
                fromView.postLayout();
            }

            if (!isToViewReady) {
                toView.postLayout();
            }
        } else {
            executePopChangeCancelable(fromInfo, toInfo, popEndAction, cancellationSignal.getChildCancellationSignal());
        }
    }

    public abstract void executePushChangeCancelable(final AnimationInfo fromInfo, final AnimationInfo toInfo, final Runnable endAction, CancellationSignal cancellationSignal);

    public abstract void executePopChangeCancelable(final AnimationInfo fromInfo, final AnimationInfo toInfo, final Runnable endAction, CancellationSignal cancellationSignal);

    private static void skipDrawUntilViewMeasureReady(final Component rootView,
                                                      CancellationSignal cancellationSignal,
                                                      final Runnable endAction) {
        final ComponentTreeObserver viewTreeObserver = rootView.getComponentTreeObserver();
        final AtomicBoolean skipDraw = new AtomicBoolean(true);

        ComponentTreeObserver.GlobalFocusUpdatedListener globalFocusUpdatedListener = new ComponentTreeObserver.GlobalFocusUpdatedListener() {
            @Override
            public void onGlobalFocusUpdated(Component component, Component component1) {
                viewTreeObserver.removeGlobalFocusUpdatedListener(this);
                skipDraw.set(false);
                endAction.run();
            }
        };

        cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
            @Override
            public void onCancel() {
                viewTreeObserver.removeGlobalFocusUpdatedListener(globalFocusUpdatedListener);
                skipDraw.set(false);
                endAction.run();
            }
        });

        viewTreeObserver.addGlobalFocusUpdatedListener(globalFocusUpdatedListener);

    }
}
